#ifndef ODBC_ENVIRONMENT_HPP
#define ODBC_ENVIRONMENT_HPP

#include "odbc/odbcpp_config.hpp"
#include "odbc/odbc_error.hpp"
#include "odbc/details/os_sql_wrapper.hpp"
#include <boost/noncopyable.hpp>
#include <boost/static_assert.hpp>
#include <cassert>

namespace odbc
{

/// \ingroup odbcpp
/// \brief It encapsulates the ODBC environment object, used to open connections to databases.
template< unsigned int Version>
class environment: boost::noncopyable
{
	BOOST_STATIC_ASSERT( Version > 0);
public:
	/// \brief It gets the ODBC version used.
	enum { version = Version };

	/// \brief Initializes the environment object using the specified version number of the ODBC system, by allocating
	///	the ODBC environment handle.
	/// \param version the version number of the ODBC system.
	environment()
		: handle_( SQL_NULL_HENV)
	{
		try
		{
			// Alloc the handle
			// TODO: is this really working? Passing this object to the exception for diagnostic gathering?
			this->check_success( SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handle_));

			// Set the odbc version
			this->check_success( SQLSetEnvAttr( handle_,
					SQL_ATTR_ODBC_VERSION, SQLPOINTER( version), 0));
		}
		catch( ...)
		{
			SQLFreeHandle( SQL_HANDLE_ENV, handle_);
		}
	}


	/// \brief It deallocates the odbc environment handle.
	~environment() throw()
	{
		// Dealloc the handle.
		// In case of error, don't throw exception.
		// It's not good for library health :)
		SQLFreeHandle( SQL_HANDLE_ENV, handle_);
	}

private:
	/// \internal
	/// \brief Stores the odbc handle to odbc environment.
	SQLHANDLE handle_;

private:
	template< unsigned int VerNo> friend class environment_visitor;

	/// \internal
	/// \brief It checks the given result code and, if it is not a success code, it throws an odbc_error containing
	///		the diagnostics for the error.
	void check_success( SQLRETURN ret_code)
	{
		if( ret_code != SQL_SUCCESS && ret_code != SQL_SUCCESS_WITH_INFO)
		{
			throw odbc_error( ret_code, handle_, SQL_HANDLE_ENV);
		}
	}
};

// TODO: Doc
template< unsigned int Version>
class environment_visitor
{
protected:
	static SQLHANDLE get_env_handle( const environment<Version>& env) { return env.handle_; }
};

} // namespace odbc

#endif // ODBC_ENVIRONMENT_HPP
